/**
 *                     Remixvym (version 0.8.3)
 *             Copyright (c) 2010 by RemixLab, DISI-UNAL
 *            http://www.disi.unal.edu.co/grupos/remixlab/
 *
 * This Java package provides classes to visualize Mind Maps created in VYM
 * (View Your Mind) in a Processing Sketch
 *
 * @author Alvaro Lopez, Juan Baquero
 *
 * This source file is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option)
 * any later version.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * A copy of the GNU General Public License is available on the World Wide Web
 * at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by
 * writing to the Free Software Foundation, 51 Franklin Street, Fifth Floor
 * Boston, MA 02110, USA
 */
package remixlab.remixvym.core.util;

import java.util.ArrayList;

import processing.core.PConstants;
import remixlab.remixvym.MindMap;
import remixlab.remixvym.core.mindmap.Node;

/**The methods of this class are used to control the navigation system and the editing mode*/
public class NavigationUtil {

	/**Returns the code of the direction in which the children of a node opens respect to its parent position
	 * The actual scrolling implementation does not support the up and down directions, but NavigationUtil does.
	 * */
	public static int getDirectionChilds(Node node)
	{
		if(node== null)
			return UNDEFINED;
		
		if(node.children== null || node.children.size()==0 || node.scrolledNow)
			return UNDEFINED;
		
		int rst =0;
		float plusX =0; //Sum of the differences in X
		int numX =0; //Count of differences in X
		float plusY =0; //Sum of the differences in Y
		int numY =0; //Count of differences in Y
		
		float pX;
		float pY;
		
		if(node.children.size()==1)
		{
			 plusY = Math.abs(node.x1 - node.children.get(0).x1);	
			 numY++;
			 
			 plusX = Math.abs(node.y1 - node.children.get(0).y1);	
			 numX++;
		}
		
		for(int i=1;i<node.children.size();i++)
		{
			 plusX = plusX +Math.abs((node.children.get(i-1).x1 - node.children.get(i).x1));	
			 numX++;
			 
			 plusY = plusY +Math.abs(node.children.get(i-1).y1 - node.children.get(i).y1);	
			 numY++;
		}
		
		pX=(float)plusX/(float)numX;
		pY=(float)plusY/(float)numY;
		
		boolean dir; //true=left false=rigth
		             //true=up   false=down
		
		if(pX<pY)
		{
			if(node.x1-node.children.get(0).x1>0) dir=true;
			else dir=false;
			
			if(dir) rst=LEFT;
			else rst=RIGHT;
			
			for(int i=1;i<node.children.size();i++)
			{
				if(node.x1-node.children.get(i).x1>0 && !dir) 
					rst=UNDEFINED;
				
				if(node.x1-node.children.get(i).x1<=0 && dir) 
					rst=UNDEFINED;
			}
			
		}else
		{
			if(node.y1-node.children.get(0).y1>0) dir=true;
			else dir=false;
			
			if(dir) rst=UP;
			else rst=DOWN;
			
			for(int i=1;i<node.children.size();i++)
			{
				if(node.y1-node.children.get(i).y1>0 && !dir) 
					rst=UNDEFINED;
				
				if(node.y1-node.children.get(i).y1<=0 && dir) 
					rst=UNDEFINED;
			}
		}
		return rst;
	}
	
	/**Moves a node, its brothers and all its children if they are visible.
	 * Returns the next node in the selected direction 
	 * */
	public static Node move(int keyCode, Node selected, MindMap map) {
		
		int dirChilds = getDirectionChilds(selected);
		int dirBrothers = getDirectionChilds(selected.getFather());
		boolean father=false;
		boolean childrens=false;
		
		if(selected.getFather()!=null)father=true;
		if(selected.children.size()!=0 && !selected.scrolledNow)childrens=true;
		
		Node next = null;
		
		switch (keyCode)
		{
			case PConstants.RIGHT:
			
				if(childrens && dirChilds==RIGHT){
					next=selected.children.get(0);break; //First child node
				}else if(childrens && dirChilds==UNDEFINED){
					next=searchNext(selected,RIGHT);break; //Find nearest node in rightwards
				}
				
				if(father && dirBrothers==LEFT){
					next=selected.getFather(); //Parent node
				}else if(father && (dirBrothers==UP || dirBrothers==DOWN)){
					next=searchBrother(selected,RIGHT);//Right brother node
				}else if(father && dirBrothers==UNDEFINED){
					next=searchNext(selected,RIGHT);//Find nearest node rightwards
				}
				break;
				
			case PConstants.LEFT:
				if(childrens && dirChilds==LEFT){
					next=selected.children.get(0);break; //First child node
				}else if(childrens && dirChilds==UNDEFINED){
					next=searchNext(selected,LEFT);break; //Find nearest node leftwards
				}
				if(father && dirBrothers==RIGHT){
					next=selected.getFather(); //Parent node
				}else if(father && (dirBrothers==UP||dirBrothers==DOWN)){
					next=searchBrother(selected,LEFT);//Left brother node
				}else if(father && dirBrothers==UNDEFINED){
					next=searchNext(selected,LEFT);//Find nearest node leftwards
				}
				break;
				
			case PConstants.UP:
				
				if(childrens && dirChilds==UP){
					next=selected.children.get(0);break;//First child node
				}else if(childrens && dirChilds==UNDEFINED){
					next=searchNext(selected,UP);break;//Find nearest node upwards
				}
				if(father && dirBrothers==DOWN){
					next=selected.getFather();//Parent node
				}else if(father && (dirBrothers==RIGHT||dirBrothers==LEFT)){
					next=searchBrother(selected,UP);//Up brother node
				}else if(father && dirBrothers==UNDEFINED){
					next=searchNext(selected,UP);//Find nearest node upwards
				}
				
				break;
				
			case PConstants.DOWN:
				
				if(childrens && dirChilds==DOWN)
				{
					next=selected.children.get(0);break;//First child node
				}else if(childrens && dirChilds==UNDEFINED){
					next=searchNext(selected,DOWN);break;//Find nearest node downwards
				}
				if(father && dirBrothers==UP){
					next=selected.getFather();//Parent node
				}else if(father && (dirBrothers==RIGHT||dirBrothers==LEFT)){
					next=searchBrother(selected,DOWN);//Down brother node
				}else if(father && dirBrothers==UNDEFINED){
					next=searchNext(selected,DOWN);//Find nearest node downwards
				}
		}
		 return next;
		
	}
	
	/**Search the nearest brother of a node in a selected direction*/
	private static Node searchBrother(Node selected, int dir) {
		ArrayList<Node> lista = selected.getFather().children;
		Node next = null;
		int i;
		float distMin = Float.MAX_VALUE;
		float dist;
		
		switch (dir)
		{
			case RIGHT: 
				for(i=0;i<lista.size();i++)
				{
					if(lista.get(i)!=selected)
					{
						dist = lista.get(i).x1-selected.x1;
						if(dist>0 && dist<distMin)
						{
							distMin=dist;
							next=lista.get(i);
						}
					}
				}
				break;
			case LEFT: 
				for(i=0;i<lista.size();i++)
				{
					if(lista.get(i)!=selected)
					{
						dist = selected.x1-lista.get(i).x1;
						if(dist>0 && dist<distMin)
						{
							distMin=dist;
							next=lista.get(i);
						}
					}
				}
			case UP: //UP
				for(i=0;i<lista.size();i++)
				{
					if(lista.get(i)!=selected)
					{
						dist = selected.y1-lista.get(i).y1;
						if(dist>0 && dist<distMin )
						{
							distMin=dist;
							next=lista.get(i);
						}
					}
				}
				break;
			case DOWN: //DOWN
				for(i=0;i<lista.size();i++)
				{
					if(lista.get(i)!=selected)
					{
						dist = lista.get(i).y1-selected.y1;
						if(dist>0 && dist<distMin)
						{
							next=lista.get(i);
							distMin=dist;
						}
					}
				}
		}
		
		return next;
	}

	/**Search the nearest Node to the node selected in a specific direction*/
	private static Node searchNext(Node selected, int dir) {
		Node next = null;
		int i;
		float distMin = Float.MAX_VALUE;
		float dist;
		
		float X;
		float Y;
		
		switch (dir)
		{
			case RIGHT: 
				if(selected.getFather()!=null)
				{
					if(selected.getFather().x1 >selected.x1)
					{
						next=selected.getFather();
					}
				}
				
				if(selected.scrolledNow) break;
				
				//SELECCIONAR EL HIJO MAS ALTO
				distMin=Integer.MAX_VALUE;
				
				for(i=0;i<selected.children.size();i++)
				{
					if(selected.children.get(i).x1>selected.x1 && selected.children.get(i).y1<distMin)
					{
							distMin=selected.children.get(i).y1;
							next=selected.children.get(i);
					}
				}
				
				break;
				
			case LEFT: 
				if(selected.getFather()!=null)
				{
					if(selected.getFather().x1 <selected.x1)
					{
						next=selected.getFather();
					}
				}
		
				if(selected.scrolledNow) break;
				
				//SELECCIONAR EL HIJO MAS ALTO
				distMin=Integer.MAX_VALUE;
				
				for(i=0;i<selected.children.size();i++)
				{
					if(selected.children.get(i).x1<selected.x1 && selected.children.get(i).y1<distMin)
					{
							distMin=selected.children.get(i).y1;
							next=selected.children.get(i);
					}
				}
				
				break;
				
				
			case UP: //UP
				if(selected.getFather()!=null)
				{	
					for(i=0;i<selected.getFather().children.size();i++)
					{
						if(selected.getFather().children.get(i)!=selected)
						{
							Y = selected.y1-selected.getFather().children.get(i).y1;
							X = selected.x1-selected.getFather().children.get(i).x1;
							dist = X*X+Y*Y;
							if(Y>0 && dist<distMin)
							{
								distMin=dist;
								next=selected.getFather().children.get(i);
							}
						}
					}
				}
				break;
				
			case DOWN: //DOWN
				if(selected.getFather()!=null)
				{
					for(i=0;i<selected.getFather().children.size();i++)
					{
						if(selected.getFather().children.get(i)!=selected)
						{
							Y = selected.getFather().children.get(i).y1-selected.y1;
							X = selected.getFather().children.get(i).x1-selected.x1;
							dist = X*X+Y*Y;
							if(Y>0 && dist<distMin)
							{
								distMin=dist;
								next=selected.getFather().children.get(i);
							}
						}
					}
				}
		}
		return next;
	}

	//Constants for directions
	public static final int UNDEFINED = 0;
	public static final int RIGHT = 1;
	public static final int LEFT = 2;
	public static final int UP = 3;
	public static final int DOWN = 4;
	
}
